package com.andy.module.util

import com.andy.network.util.AndyDns
import com.andy.module.network.ApiResponse
import com.andy.module.network.SimpleApiService
import com.andy.module.progress.DownloadProgressListener
import com.andy.module.progress.ProgressRequestBody
import com.andy.module.progress.ProgressResponseBody
import com.andy.module.progress.UploadProgressListener
import com.blankj.utilcode.util.GsonUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import java.io.*
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit


/**
 * @creator:LuoFan
 * @date:2022/6/9
 * @description:
 */
object DownloadUtil {

    private val okHttpClient: OkHttpClient
        get() {
            return OkHttpClient.Builder()
                .addInterceptor(HttpLoggingInterceptor())
                .dns(AndyDns())
                .connectTimeout(15, TimeUnit.SECONDS)
                .readTimeout(10, TimeUnit.SECONDS)
                .writeTimeout(10, TimeUnit.SECONDS).build()
        }

    private val retrofit: Retrofit
        get() {
            return Retrofit.Builder()
                .baseUrl("http://www.xxx.com") //通过线程池获取一个线程，指定callback在子线程中运行。
                .callbackExecutor(Executors.newSingleThreadExecutor())
                .client(okHttpClient)
                .build()
        }

    fun download(url: String, saveFilePath: String, fileName: String, listener: DownloadProgressListener) {
        GlobalScope.launch(Dispatchers.IO) {
            val okHttpClient = okHttpClient
            val request = Request.Builder().url(url).get().build()
            val response = okHttpClient.newCall(request).execute()
            response.body?.let {
                val body = ProgressResponseBody(it, listener)
                val file = saveToFile(body, saveFilePath, fileName)
                listener.onDownloadSuccess(file)
            } ?: let {
                listener.onError(Throwable("ResponseBody is null"))
            }
        }
    }

    fun download2(url: String, saveFilePath: String, fileName: String, listener: DownloadProgressListener) {
        val call = retrofit.create(SimpleApiService::class.java).downloadFile(url)
        call.enqueue(object : retrofit2.Callback<ResponseBody> {
            override fun onResponse(call: retrofit2.Call<ResponseBody>, response: retrofit2.Response<ResponseBody>) {
                response.body()?.let {
                    val body = ProgressResponseBody(it, listener)
                    val file = saveToFile(body, saveFilePath, fileName)
                    listener.onDownloadSuccess(file)
                }
            }

            override fun onFailure(call: retrofit2.Call<ResponseBody>, error: Throwable) {
                listener.onError(error)
            }
        })
    }


    fun upload(url: String, filePath: String, params: HashMap<String, String>, listener: UploadProgressListener): Call {
        val file = File(filePath)
        val requestBuilder = Request.Builder().url(url)
        val bodyBuilder = MultipartBody.Builder()
            .setType(MultipartBody.FORM)
        val fileMediaType = "multipart/form-data".toMediaTypeOrNull()
        bodyBuilder.addFormDataPart("file", file.name, RequestBody.create(fileMediaType, file))
        params.keys.forEach {
            bodyBuilder.addFormDataPart(it, params[it]!!)
        }
        val body = bodyBuilder.build()
        val requestBody = ProgressRequestBody(body, listener)
        requestBuilder.post(requestBody)
        val call = okHttpClient.newCall(requestBuilder.build())
        call.enqueue(object : Callback {
            override fun onFailure(call: Call, error: IOException) {
                listener.onError(error)
            }

            override fun onResponse(call: Call, response: Response) {
                val jsonResult = response.body?.string()
                val result = GsonUtils.fromJson(jsonResult, ApiResponse<String>().javaClass)
                if (result.isSuccess() && result.content != null) {
                    listener.onUploadSuccess(result.content)
                } else {
                    listener.onError(Throwable(result.errorMsg))
                }
            }
        })
        return call
    }

    fun upload2(url: String, filePath: String, jsonParam: HashMap<String, String>, listener: UploadProgressListener) {
        val file = File(filePath)
        val requestBuilder = Request.Builder().url(url)
        val bodyBuilder = MultipartBody.Builder()
        val fileMediaType = "multipart/form-data".toMediaTypeOrNull()
        bodyBuilder.addFormDataPart("fileName", file.name, RequestBody.create(fileMediaType, file))

        val body = bodyBuilder.build()
        val requestBody = ProgressRequestBody(body, listener)
        requestBuilder.post(requestBody)

        val params = HashMap<String, RequestBody>()
        params["file"] = requestBody
        jsonParam.keys.forEach {
            params[it] = genBody(jsonParam[it]!!)
        }
        val call = retrofit.create(SimpleApiService::class.java).uploadFile(url, params)
        call.enqueue(object : retrofit2.Callback<ResponseBody> {
            override fun onResponse(call: retrofit2.Call<ResponseBody>, response: retrofit2.Response<ResponseBody>) {
                val jsonResult = response.body()?.string()
                val result = GsonUtils.fromJson(jsonResult, ApiResponse<String>().javaClass)
                result.content?.let {
                    listener.onUploadSuccess(it)
                }
            }

            override fun onFailure(call: retrofit2.Call<ResponseBody>, error: Throwable) {
                listener.onError(error)
            }
        })
    }

    private fun genBody(value: String): RequestBody {
        return RequestBody.create("application/json; charset=utf-8".toMediaTypeOrNull(), value)
    }

    private fun saveToFile(responseBody: ResponseBody, filePath: String, fileName: String): File {
        val inputStream: InputStream = responseBody.byteStream()
        val file = File(filePath, fileName)
        if (file.exists()) {
            file.delete()
        }
        val fos = FileOutputStream(file)
        val bis = BufferedInputStream(inputStream)
        val buffer = ByteArray(1024)
        var len: Int
        while (bis.read(buffer).also { len = it } != -1) {
            fos.write(buffer, 0, len)
        }
        fos.flush()
        fos.close()
        bis.close()
        inputStream.close()
        return file
    }
}